package org.andengine.extension.svg.util;

import android.graphics.Matrix;

import org.andengine.extension.svg.exception.SVGParseException;
import org.andengine.extension.svg.util.SVGNumberParser.SVGNumberParserFloatResult;
import org.andengine.extension.svg.util.constants.ISVGConstants;

import java.util.regex.Matcher;
import java.util.regex.Pattern;


/**
 * @author Larva Labs, LLC
 *         (c) 2010 Nicolas Gramlich
 *         (c) 2011 Zynga Inc.
 * @author Nicolas Gramlich
 * @since 16:56:54 - 21.05.2011
 */
public class SVGTransformParser implements ISVGConstants {
    // ===========================================================
    // Constants
    // ===========================================================

    private static final Pattern MULTITRANSFORM_PATTERN = Pattern.compile("(\\w+\\([\\d\\s\\-eE,]*\\))");

    // ===========================================================
    // Fields
    // ===========================================================

    // ===========================================================
    // Constructors
    // ===========================================================

    // ===========================================================
    // Getter & Setter
    // ===========================================================

    // ===========================================================
    // Methods for/from SuperClass/Interfaces
    // ===========================================================

    // ===========================================================
    // Methods
    // ===========================================================

    public static Matrix parseTransform(final String pString) {
        if (pString == null) {
            return null;
        }

		/* If ')' is contained only once, we have a simple/single transform.
         * Otherwise, we have to split multi-transforms like this:
		 * "translate(-10,-20) scale(2) rotate(45) translate(5,10)". */
        final boolean singleTransform = pString.indexOf(')') == pString.lastIndexOf(')');
        if (singleTransform) {
            return SVGTransformParser.parseSingleTransform(pString);
        } else {
            return SVGTransformParser.parseMultiTransform(pString);
        }
    }

    private static Matrix parseMultiTransform(final String pString) {
        final Matcher matcher = MULTITRANSFORM_PATTERN.matcher(pString);

        final Matrix matrix = new Matrix();
        while (matcher.find()) {
            matrix.preConcat(SVGTransformParser.parseSingleTransform(matcher.group(1)));
        }
        return matrix;
    }

    private static Matrix parseSingleTransform(final String pString) {
        try {
            if (pString.startsWith(ATTRIBUTE_TRANSFORM_VALUE_MATRIX)) {
                return SVGTransformParser.parseTransformMatrix(pString);
            } else if (pString.startsWith(ATTRIBUTE_TRANSFORM_VALUE_TRANSLATE)) {
                return SVGTransformParser.parseTransformTranslate(pString);
            } else if (pString.startsWith(ATTRIBUTE_TRANSFORM_VALUE_SCALE)) {
                return SVGTransformParser.parseTransformScale(pString);
            } else if (pString.startsWith(ATTRIBUTE_TRANSFORM_VALUE_SKEW_X)) {
                return SVGTransformParser.parseTransformSkewX(pString);
            } else if (pString.startsWith(ATTRIBUTE_TRANSFORM_VALUE_SKEW_Y)) {
                return SVGTransformParser.parseTransformSkewY(pString);
            } else if (pString.startsWith(ATTRIBUTE_TRANSFORM_VALUE_ROTATE)) {
                return SVGTransformParser.parseTransformRotate(pString);
            } else {
                throw new SVGParseException("Unexpected transform type: '" + pString + "'.");
            }
        } catch (final SVGParseException e) {
            throw new SVGParseException("Could not parse transform: '" + pString + "'.", e);
        }
    }

    public static Matrix parseTransformRotate(final String pString) {
        final SVGNumberParserFloatResult svgNumberParserFloatResult = SVGNumberParser.parseFloats(pString.substring(ATTRIBUTE_TRANSFORM_VALUE_ROTATE.length() + 1, pString.indexOf(')')));
        SVGTransformParser.assertNumberParserResultNumberCountMinimum(svgNumberParserFloatResult, 1);

        final float angle = svgNumberParserFloatResult.getNumber(0);
        float cx = 0;
        float cy = 0;
        if (svgNumberParserFloatResult.getNumberCount() > 2) {
            cx = svgNumberParserFloatResult.getNumber(1);
            cy = svgNumberParserFloatResult.getNumber(2);
        }
        final Matrix matrix = new Matrix();
        matrix.postTranslate(cx, cy);
        matrix.postRotate(angle);
        matrix.postTranslate(-cx, -cy);
        return matrix;
    }

    private static Matrix parseTransformSkewY(final String pString) {
        final SVGNumberParserFloatResult svgNumberParserFloatResult = SVGNumberParser.parseFloats(pString.substring(ATTRIBUTE_TRANSFORM_VALUE_SKEW_Y.length() + 1, pString.indexOf(')')));
        SVGTransformParser.assertNumberParserResultNumberCountMinimum(svgNumberParserFloatResult, 1);

        final float angle = svgNumberParserFloatResult.getNumber(0);
        final Matrix matrix = new Matrix();
        matrix.postSkew(0, (float) Math.tan(angle));
        return matrix;
    }

    private static Matrix parseTransformSkewX(final String pString) {
        final SVGNumberParserFloatResult svgNumberParserFloatResult = SVGNumberParser.parseFloats(pString.substring(ATTRIBUTE_TRANSFORM_VALUE_SKEW_X.length() + 1, pString.indexOf(')')));
        SVGTransformParser.assertNumberParserResultNumberCountMinimum(svgNumberParserFloatResult, 1);

        final float angle = svgNumberParserFloatResult.getNumber(0);
        final Matrix matrix = new Matrix();
        matrix.postSkew((float) Math.tan(angle), 0);
        return matrix;
    }

    private static Matrix parseTransformScale(final String pString) {
        final SVGNumberParserFloatResult svgNumberParserFloatResult = SVGNumberParser.parseFloats(pString.substring(ATTRIBUTE_TRANSFORM_VALUE_SCALE.length() + 1, pString.indexOf(')')));
        SVGTransformParser.assertNumberParserResultNumberCountMinimum(svgNumberParserFloatResult, 1);
        final float sx = svgNumberParserFloatResult.getNumber(0);
        float sy = 0;
        if (svgNumberParserFloatResult.getNumberCount() > 1) {
            sy = svgNumberParserFloatResult.getNumber(1);
        }
        final Matrix matrix = new Matrix();
        matrix.postScale(sx, sy);
        return matrix;
    }

    private static Matrix parseTransformTranslate(final String pString) {
        final SVGNumberParserFloatResult svgNumberParserFloatResult = SVGNumberParser.parseFloats(pString.substring(ATTRIBUTE_TRANSFORM_VALUE_TRANSLATE.length() + 1, pString.indexOf(')')));
        SVGTransformParser.assertNumberParserResultNumberCountMinimum(svgNumberParserFloatResult, 1);
        final float tx = svgNumberParserFloatResult.getNumber(0);
        float ty = 0;
        if (svgNumberParserFloatResult.getNumberCount() > 1) {
            ty = svgNumberParserFloatResult.getNumber(1);
        }
        final Matrix matrix = new Matrix();
        matrix.postTranslate(tx, ty);
        return matrix;
    }

    private static Matrix parseTransformMatrix(final String pString) {
        final SVGNumberParserFloatResult svgNumberParserFloatResult = SVGNumberParser.parseFloats(pString.substring(ATTRIBUTE_TRANSFORM_VALUE_MATRIX.length() + 1, pString.indexOf(')')));
        SVGTransformParser.assertNumberParserResultNumberCount(svgNumberParserFloatResult, 6);
        final Matrix matrix = new Matrix();
        matrix.setValues(new float[]{
                // Row 1
                svgNumberParserFloatResult.getNumber(0),
                svgNumberParserFloatResult.getNumber(2),
                svgNumberParserFloatResult.getNumber(4),
                // Row 2
                svgNumberParserFloatResult.getNumber(1),
                svgNumberParserFloatResult.getNumber(3),
                svgNumberParserFloatResult.getNumber(5),
                // Row 3
                0,
                0,
                1,
        });
        return matrix;
    }

    private static void assertNumberParserResultNumberCountMinimum(final SVGNumberParserFloatResult pSVGNumberParserFloatResult, final int pNumberParserResultNumberCountMinimum) {
        final int svgNumberParserFloatResultNumberCount = pSVGNumberParserFloatResult.getNumberCount();
        if (svgNumberParserFloatResultNumberCount < pNumberParserResultNumberCountMinimum) {
            throw new SVGParseException("Not enough data. Minimum Expected: '" + pNumberParserResultNumberCountMinimum + "'. Actual: '" + svgNumberParserFloatResultNumberCount + "'.");
        }
    }

    private static void assertNumberParserResultNumberCount(final SVGNumberParserFloatResult pSVGNumberParserFloatResult, final int pNumberParserResultNumberCount) {
        final int svgNumberParserFloatResultNumberCount = pSVGNumberParserFloatResult.getNumberCount();
        if (svgNumberParserFloatResultNumberCount != pNumberParserResultNumberCount) {
            throw new SVGParseException("Unexpected number count. Expected: '" + pNumberParserResultNumberCount + "'. Actual: '" + svgNumberParserFloatResultNumberCount + "'.");
        }
    }

    // ===========================================================
    // Inner and Anonymous Classes
    // ===========================================================
}
